<?php

/**
 * 使用 $Credis=Credis::init();
 * $Credis->set(key,value,time)
 * $Credis->get(key)
 * $Credis->pipe(keys)
 * $Credis->delete(key)
 * $Credis->flush(key)
 * $Credis->has(key)
 *
 * $Credis->rpush(key,value) 在列表中添加一个值到列表尾部
 * $Credis->lpop(key) 移出并获取列表的第一个元素
 * $Credis->lpush(key,value) 在列表中添加一个值到列表头部
 * $Credis->rpop(key) 移出并获取列表尾部的第一个元素
 * $Credis->llen(key) 获取列表长度
 * $Credis->lrange(key,start,end) 获取列表范围值
 * $Credis->slice(key,start,length) 获取列表范围值
 *
 * 在指定的目录下生成 $key.cache 文件，为缓存数据文件
 */
class Credis
{
    static private $path = __DIR__ . DIRECTORY_SEPARATOR . 'cache';
    static private $instance;
    static private $cache_time = 30;
    static private $time;
    /**
     * 使程序单例运行
     * @return instance
     */
    static public function init()
    {
        if (!self::$instance instanceof self) {
            self::$instance = new self;
            if (!is_dir(static::$path)) {
                $mode = intval('0777', 8);
                mkdir(static::$path, $mode, true);
            }
            self::$time = time();
            $clear_time = static::get('sys_clear_time', 0);
            if (!$clear_time || static::$time - $clear_time > static::$cache_time) {
                static::clear();
            }
        }
        return self::$instance;
    }
    /**
     * 获取缓存文件路径
     * @param $key key
     * @return string
     */
    static private function _getpath($key)
    {
        $key_parse = explode('_', $key);
        $file = static::$path . DIRECTORY_SEPARATOR . $key_parse[0] . '.cache';
        return $file;
    }

    /**
     * 设置缓存
     * @param     $key  key
     * @param     $value  value
     * @param int $time 秒
     * @return bool
     */
    static public function set($key, $value, $time = 0)
    {
        $file = static::_getpath($key);
        $data = @json_decode(@file_get_contents($file), true) ?? [];
        $data[$key] = [
            'time' => $time ? time() + $time : 0,
            'value' => $value
        ];
        return !!@file_put_contents($file, json_encode($data, JSON_UNESCAPED_UNICODE));
    }

    /**
     * 获取缓存
     * @param $key
     * @param $default mix
     * @return mix
     */
    static public function get($key, $default = null)
    {
        $file = static::_getpath($key);
        if (!file_exists($file)) {
            return $default;
        }
        $data = @json_decode(@file_get_contents($file), true) ?? [];
        if (!isset($data[$key])) {
            return $default;
        }
        if ($data[$key]['time']) {
            if ($data[$key]['time'] > time()) {
                return $data[$key]['value'];
            } else {
                unset($data[$key]);
                @file_put_contents($file, json_encode($data, JSON_UNESCAPED_UNICODE));
                return $default;
            }
        } else {
            return $data[$key]['value'];
        }
    }

    /**
     * 批量获取缓存
     * @param $keys
     * @param $default mix
     * @return array
     */
    static public function pipe($keys, $default = null)
    {
        $files = [];
        $rs = [];
        foreach ($keys as $v) {
            $file = static::_getpath($v);
            $files[$file] = $files[$file] ?? [];
            $files[$file][] = $v;
            $rs[$v] = $default;
        }
        $time = time();
        foreach ($files as $file => $ks) {
            if (file_exists($file)) {
                $data = @json_decode(@file_get_contents($file), true) ?? [];
                foreach ($ks as $key) {
                    if (isset($data[$key])) {
                        if (!$data[$key]['time'] || $data[$key]['time'] > $time) {
                            $rs[$key] = $data[$key]['value'];
                        }
                    }
                }
            }
        }
        return $rs;
    }

    /**
     * 删除缓存
     * @param $key
     * @return bool
     */
    static public function delete($key)
    {
        $file = static::_getpath($key);
        if (!file_exists($file)) {
            return true;
        }
        $data = @json_decode(@file_get_contents($file), true) ?? [];
        if (!isset($data[$key])) {
            return true;
        }
        unset($data[$key]);
        return !!@file_put_contents($file, json_encode($data, JSON_UNESCAPED_UNICODE));
    }

    /**
     * 获取后删除
     * @param $key
     * @return null
     */
    static public function flush($key)
    {
        $file = static::_getpath($key);
        if (!file_exists($file)) {
            return null;
        }
        $data = @json_decode(@file_get_contents($file), true) ?? [];
        if (!isset($data[$key])) {
            return null;
        }
        if ($data[$key]['time']) {
            if ($data[$key]['time'] > time()) {
                $rs = $data[$key]['value'];
            } else {
                $rs = null;
            }
        } else {
            $rs = $data[$key]['value'];
        }
        unset($data[$key]);
        @file_put_contents($file, json_encode($data, JSON_UNESCAPED_UNICODE));
        return $rs;
    }

    /**
     * 判断缓存是否存在
     * @param $key
     * @return bool
     */
    static public function has($key)
    {
        $rs = static::get($key);
        return !is_null($rs);
    }

    /**
     * 在列表中添加一个值到列表尾部
     * @param     $key  key
     * @param     $value  value
     * @return bool
     */
    static public function rpush($key, $value)
    {
        return static::_push($key, $value, 'right');
    }
    /**
     * 在列表中添加一个值到列表头部
     * @param     $key  key
     * @param     $value  value
     * @return bool
     */
    static public function lpush($key, $value)
    {
        return static::_push($key, $value, 'left');
    }
    /**
     * 处理入栈
     */
    static private function _push($key, $value, $type = 'left')
    {
        $key = 'list@' . $key;
        $file = static::_getpath($key);
        $list = [];
        if (file_exists($file)) {
            $data = @json_decode(@file_get_contents($file), true) ?? [];
            if (isset($data[$key]) && is_array($data[$key]['value'])) {
                $list = $data[$key]['value'];
            }
        }
        if ($type == 'left') {
            array_unshift($list, $value); //在数组开头插入一个或多个元素
        } else {
            array_push($list, $value); //将一个或多个元素插入数组的末尾
        }
        $data[$key] = [
            'time' => 0,
            'value' => $list
        ];
        return !!@file_put_contents($file, json_encode($data, JSON_UNESCAPED_UNICODE));
    }
    /**
     * 从列表头部取一个值
     * @param $key
     * @return mix
     */
    static public function lpop($key)
    {
        return static::_pop($key, 'left');
    }
    /**
     * 从列表尾部取一个值
     * @param $key
     * @return mix
     */
    static public function rpop($key)
    {
        return static::_pop($key, 'right');
    }
    /**
     * 处理出栈
     */
    static private function _pop($key, $type = 'right')
    {
        $key = 'list@' . $key;
        $file = static::_getpath($key);
        $list = [];
        if (file_exists($file)) {
            $data = @json_decode(@file_get_contents($file), true) ?? [];
            if (isset($data[$key]) && is_array($data[$key]['value'])) {
                $list = $data[$key]['value'];
            }
        }
        if (empty($list)) {
            return null;
        }
        if ($type == 'right') {
            $row = array_pop($list); //删除数组中的最后一个元素
        } else {
            $row = array_shift($list); //删除数组中的第一个元素，并返回被删除元素的值
        }
        $data[$key] = [
            'time' => 0,
            'value' => $list
        ];
        @file_put_contents($file, json_encode($data, JSON_UNESCAPED_UNICODE));
        return $row;
    }
    /**
     * 获取列表值
     */
    static private function _list($file, $key)
    {
        $list = [];
        if (file_exists($file)) {
            $data = @json_decode(@file_get_contents($file), true) ?? [];
            if (isset($data[$key]) && is_array($data[$key]['value'])) {
                $list = $data[$key]['value'];
            }
        }
        return $list;
    }
    /**
     * 获取列表长度
     * @param $key
     * @return int
     */
    static public function llen($key)
    {
        $key = 'list@' . $key;
        $file = static::_getpath($key);
        $list = static::_list($file, $key);
        return empty($list) ? 0 : count($list);
    }
    /**
     * 获取列表范围值
     * @param $key
     * @param $start
     * @param $end
     * @return array
     * redis中end=-2包含-2位置的值
     */
    static public function lrange($key, $start = null, $end = null)
    {
        $key = 'list@' . $key;
        $file = static::_getpath($key);
        $list = static::_list($file, $key);
        $llen = empty($list) ? 0 : count($list);
        $start = is_null($start) ? 0 : ($start >= 0 ? $start : max(0, $llen + $start));
        $end = is_null($end) ? $llen : ($end >= 0 ? $end : max(0, $llen + $end));
        $start = min($start, $llen);
        $end = min($llen, $end);
        if ($start > $end) {
            return [];
        }
        return $end < $start ? [] : array_slice($list, $start, $end + 1 - $start);
    }
    /**
     * 获取列表范围值
     * @param $key
     * @param $start
     * @param $length
     * @return array
     * php中end=-2不包含-2位置的值
     */
    static public function slice($key, $start = null, $length = null)
    {
        $key = 'list@' . $key;
        $file = static::_getpath($key);
        $list = static::_list($file, $key);
        return array_slice($list, $start, $length);
    }
    /**
     * 清理长期不用的文件
     */
    static public function clear()
    {
        $cache_time = static::$cache_time;
        $time =static::$time;
        static::set('sys_clear_time', $time, $cache_time * 2);
        $dir = static::$path . DIRECTORY_SEPARATOR . '/';
        if (is_dir($dir)) {
            $list = scandir($dir, 1);
            if ($list) {
                $del_time = $time - $cache_time;
                foreach ($list as $v) {
                    if ($v != '.' && $v != '..' && is_file($dir . $v)) {
                        if (filectime($dir . $v) < $del_time) {
                            @unlink($dir . $v); //删除文件
                        }
                    }
                }
            }
            //@rmdir($dir);
        }
    }
}
